package com.william.jdbcplus.generator;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import com.william.jdbcplus.core.dialects.meta.MetaUtil;
import com.william.jdbcplus.core.generator.EntityGenerator;
import com.william.jdbcplus.core.generator.GeneratorConfig;
import com.william.jdbcplus.core.generator.meta.EntityTableInfo;
import com.william.jdbcplus.core.generator.meta.TableToEntityUtil;
import com.william.jdbcplus.core.toolkit.StringPool;
import com.william.jdbcplus.core.util.JdbcUtil;
import com.william.jdbcplus.core.util.datameta.TableMetaInfo;
import com.william.jdbcplus.generator.exception.CheckedException;
import com.william.jdbcplus.generator.vo.ConfigTableVo;
import com.william.jdbcplus.generator.vo.ConfigVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import javax.sql.DataSource;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 生成实体 工具类
 *
 * @author william
 */
@Slf4j
public class EntityGeneratorUtil {
    private static final String BASE_PATH = "template/";
    private static final String ENTITY_JAVA_VM = "Entity.java.vm";

    private static List<String> getTemplates() {
        List<String> templates = new ArrayList<>();
        templates.add(BASE_PATH + ENTITY_JAVA_VM);
        return templates;
    }

    /**
     * 生成代码
     */
    public static void generatorCode(GeneratorConfig genConfig, ZipOutputStream zip) {
        // 设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        // 封装模板数据
        Map<String, Object> map = new HashMap<>();
        //表格信息
        TableMetaInfo tableInfo = MetaUtil.getTableInfo(genConfig.getDataSource(), genConfig.getTableName());
        if (tableInfo == null) {
            return;
        }
        EntityTableInfo entityTableInfo = TableToEntityUtil.convert(tableInfo, genConfig.getTablePrefix());
        map.put("entity", EntityGenerator.generator(genConfig, entityTableInfo));

        VelocityContext context = new VelocityContext(map);

        // 获取模板列表
        List<String> templates = getTemplates();
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
            tpl.merge(context, sw);

            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getFileName(template,
                        entityTableInfo.getEntityName(),
                        genConfig.getPackageName(),
                        genConfig.getModuleName()))));
                IoUtil.write(zip, CharsetUtil.UTF_8, false, sw.toString());
                IoUtil.close(sw);
                zip.closeEntry();
            } catch (IOException e) {
                throw new CheckedException("渲染模板失败，表名：" + genConfig.getTableName(), e);
            }
        }
    }

    /**
     * 获取文件名
     */
    private static String getFileName(String template, String className, String packageName, String moduleName) {
        String packagePath = StringPool.EMPTY;
        if (StringUtils.isNotBlank(packageName)) {
            packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
        }

        if (template.contains(ENTITY_JAVA_VM)) {
            return packagePath + "entity" + File.separator + className + ".java";
        }

        return null;
    }

    public static byte[] entityCode(ConfigTableVo config) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        GeneratorConfig generatorConfig = new GeneratorConfig();
        generatorConfig.setDataSource(config.getDataSource());
        generatorConfig.setAuthor(config.getAuthor());
        generatorConfig.setModuleName(config.getModuleName());
        generatorConfig.setPackageName(config.getPackageName());
        generatorConfig.setTablePrefix(config.getTablePrefix());
        for (String tableName : config.getTableNames()) {
            generatorConfig.setTableName(tableName);
            generatorCode(generatorConfig, zip);
        }
        IoUtil.close(zip);
        return outputStream.toByteArray();
    }

    public static byte[] entityCodes(ConfigVO config) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        GeneratorConfig generatorConfig = new GeneratorConfig();
        generatorConfig.setDataSource(config.getDataSource());
        generatorConfig.setAuthor(config.getAuthor());
        generatorConfig.setModuleName(config.getModuleName());
        generatorConfig.setPackageName(config.getPackageName());
        generatorConfig.setTablePrefix(config.getTablePrefix());
        List<String> tableNameList = JdbcUtil.getTables(config.getDataSource());
        for (String tableName : tableNameList) {
            generatorConfig.setTableName(tableName);
            generatorCode(generatorConfig, zip);
        }
        IoUtil.close(zip);
        return outputStream.toByteArray();
    }

    /**
     * bfile 需要转换成文件的byte数组
     * filePath  生成的文件保存路径
     * fileName  生成文件后保存的名称如test.pdf，test.jpg等
     */
    public static void outputFile(byte[] bfile, String filePath, String fileName) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            boolean isDir = dir.isDirectory();
            if (!isDir) { // 目录不存在则先建目录
                try {
                    dir.mkdirs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            file = new File(filePath + File.separator + fileName);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bfile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public static DataSource getDs() {
        String url = "jdbc:mysql://192.168.0.98:3333/his_test?serverTimezone=UTC";
        String username = "root";
        String password = "123456";
        DataSource dataSource = JdbcUtil.createDataSource(url, username, password);
        return dataSource;
    }

    public static void main(String[] args) {
        // 生成指定表名列表对应的实体
        ConfigTableVo vo = new ConfigTableVo();
        vo.setAuthor("william");
        vo.setDataSource(getDs());
        //实体所在包名前缀
        vo.setPackageName("com.william.jdbcplus");
        //模块名
        vo.setModuleName("generator");
        List<String> tables = new ArrayList<>();
        // 必须与数据库大小写一致
        tables.add("excel_report");
        tables.add("excel_report_data_source");
        tables.add("excel_report_db");
        tables.add("excel_report_db_field");
        tables.add("excel_report_db_param");
        vo.setTableNames(tables);
        vo.setTablePrefix("");
        byte[] data = entityCode(vo);
        outputFile(data, "D:\\temp", "report.zip");

        //生成数据库所有表对应的实体列表
        ConfigVO configVO = new ConfigVO();
        configVO.setAuthor("william");
        configVO.setDataSource(getDs());
        //实体所在包名前缀
        configVO.setPackageName("com.william.jdbcplus");
        //模块名
        configVO.setModuleName("generator");
        configVO.setTablePrefix("");
        byte[] dataList = entityCodes(configVO);
        outputFile(dataList, "D:\\temp", "reportAll.zip");
    }
}
